├── .editorconfig
├── .gitignore
├── .idea
├── icon.png
├── inspectionProfiles
│ └── Project_Default.xml
├── kotlinc.xml
├── ktlint-plugin.xml
├── ktlint.xml
├── markdown.xml
└── vcs.xml
├── LICENSE
├── README-zh-CN.md
├── README.md
├── build.gradle.kts
├── docs
├── changelog-zh-CN.md
├── changelog.md
├── guide-zh-CN.md
└── guide.md
├── flexilocale-gradle-plugin
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── com
│ └── highcapable
│ └── flexilocale
│ ├── FlexiLocale.kt
│ ├── gradle
│ ├── factory
│ │ ├── ExtensionAwareFactory.kt
│ │ └── GradleProjectFactory.kt
│ └── proxy
│ │ └── IProjectLifecycle.kt
│ ├── plugin
│ ├── FlexiLocaleExtension.kt
│ ├── FlexiLocalePlugin.kt
│ ├── config
│ │ └── proxy
│ │ │ └── IFlexiLocaleConfigs.kt
│ ├── extension
│ │ └── dsl
│ │ │ └── configure
│ │ │ └── FlexiLocaleConfigureExtension.kt
│ ├── generator
│ │ ├── LocaleSourcesGenerator.kt
│ │ └── factory
│ │ │ └── GeneratorFactory.kt
│ └── helper
│ │ └── LocaleAnalysisHelper.kt
│ └── utils
│ ├── debug
│ ├── FError.kt
│ └── FLog.kt
│ └── factory
│ ├── FileFactory.kt
│ └── VariableFactory.kt
├── gradle.properties
├── gradle
├── sweet-dependency
│ └── sweet-dependency-config.yaml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── img-src
└── icon.png
└── settings.gradle.kts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # noinspection EditorConfigKeyCorrectness
2 | [{*.kt,*.kts}]
3 | ktlint_standard_annotation = disabled
4 | ktlint_standard_filename = disabled
5 | ktlint_standard_wrapping = disabled
6 | ktlint_standard_import-ordering = enabled
7 | ktlint_standard_max-line-length = disabled
8 | ktlint_standard_multiline-if-else = disabled
9 | ktlint_standard_argument-list-wrapping = disabled
10 | ktlint_standard_parameter-list-wrapping = disabled
11 | ktlint_standard_trailing-comma-on-declaration-site = disabled
12 | ktlint_function_signature_body_expression_wrapping = multiline
13 | ktlint_standard_string-template-indent = disabled
14 | ktlint_standard_function-signature = disabled
15 | ktlint_standard_trailing-comma-on-call-site = disabled
16 | ktlint_standard_multiline-expression-wrapping = disabled
17 | ktlint_standard_no-empty-first-line-in-class-body = disabled
18 | ktlint_standard_if-else-wrapping = disabled
19 | ktlint_standard_if-else-bracing = disabled
20 | ktlint_standard_statement-wrapping = disabled
21 | ktlint_standard_blank-line-before-declaration = disabled
22 | ktlint_standard_no-empty-file = disabled
23 | ktlint_standard_property-naming = disabled
24 | ktlint_standard_function-naming = disabled
25 | ktlint_standard_chain-method-continuation = disabled
26 | ktlint_standard_class-signature = disabled
27 | ktlint_standard_condition-wrapping = disabled
28 | ktlint_standard_class-signature = disabled
29 | ktlint_standard_no-trailing-spaces = disabled
30 | ktlint_standard_multiline-loop = disabled
31 | ij_continuation_indent_size = 2
32 | indent_size = 4
33 | indent_style = space
34 | insert_final_newline = false
35 | max_line_length = 150
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects
2 | ## References:
3 | ## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
4 | ## - https://github.com/android/platform-samples/blob/main/.gitignore
5 |
6 | # User-specific stuff
7 | .idea/**/workspace.xml
8 | .idea/**/tasks.xml
9 | .idea/**/usage.statistics.xml
10 | .idea/**/dictionaries
11 | .idea/**/shelf
12 |
13 | # AWS User-specific
14 | .idea/**/aws.xml
15 |
16 | # Generated files
17 | .idea/**/contentModel.xml
18 |
19 | # Sensitive or high-churn files
20 | .idea/**/dataSources/
21 | .idea/**/dataSources.ids
22 | .idea/**/dataSources.local.xml
23 | .idea/**/sqlDataSources.xml
24 | .idea/**/dynamic.xml
25 | .idea/**/uiDesigner.xml
26 | .idea/**/dbnavigator.xml
27 |
28 | # Gradle
29 | .idea/**/gradle.xml
30 | .idea/**/libraries
31 |
32 | # Gradle and Maven with auto-import
33 | .idea/.name
34 | .idea/artifacts
35 | .idea/compiler.xml
36 | .idea/jarRepositories.xml
37 | .idea/modules.xml
38 | .idea/*.iml
39 | .idea/modules
40 | .idea/caches
41 | .idea/material_theme**
42 | .idea/other.xml
43 | *.iml
44 | *.ipr
45 |
46 | # Kotlin
47 | .kotlin
48 |
49 | # Misc
50 | .idea/misc.xml
51 |
52 | # CMake
53 | cmake-build-*/
54 |
55 | # Mongo Explorer plugin
56 | .idea/**/mongoSettings.xml
57 |
58 | # File-based project format
59 | *.iws
60 |
61 | # IntelliJ
62 | out/
63 |
64 | # mpeltonen/sbt-idea plugin
65 | .idea_modules/
66 |
67 | # JIRA plugin
68 | atlassian-ide-plugin.xml
69 |
70 | # Cursive Clojure plugin
71 | .idea/replstate.xml
72 |
73 | # SonarLint plugin
74 | .idea/sonarlint/
75 |
76 | # Crashlytics plugin (for Android Studio and IntelliJ)
77 | com_crashlytics_export_strings.xml
78 | crashlytics.properties
79 | crashlytics-build.properties
80 | fabric.properties
81 |
82 | # Editor-based Rest Client
83 | .idea/httpRequests
84 |
85 | # Android studio 3.1+ serialized cache file
86 | .idea/caches/build_file_checksums.ser
87 |
88 | # Android studio 3.1+ additional
89 | .idea/deployment*.xml
90 | .idea/assetWizardSettings.xml
91 | .idea/androidTestResultsUserPreferences.xml
92 |
93 | # Android projects
94 | **/local.properties
95 | /captures
96 | .externalNativeBuild
97 | .cxx
98 |
99 | # Gradle projects
100 | .gradle
101 | build/
102 |
103 | # Mkdocs temporary serving folder
104 | docs-gen
105 | site
106 | *.bak
107 | .idea/appInsightsSettings.xml
108 |
109 | # Mac OS
110 | .DS_Store
--------------------------------------------------------------------------------
/.idea/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BetterAndroid/FlexiLocale/56ff6a109aaecb851b09c82b6f4170a3c33fad10/.idea/icon.png
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/ktlint-plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MANUAL
5 | false
6 |
7 |
--------------------------------------------------------------------------------
/.idea/ktlint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 |
6 |
--------------------------------------------------------------------------------
/.idea/markdown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright HighCapable [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | https://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README-zh-CN.md:
--------------------------------------------------------------------------------
1 | # Flexi Locale
2 |
3 | [](https://github.com/BetterAndroid/FlexiLocale/blob/master/LICENSE)
4 | [](https://github.com/BetterAndroid/FlexiLocale/releases)
5 | [](https://t.me/BetterAndroid)
6 | [](https://t.me/HighCapable_Dev)
7 | [](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf)
8 |
9 |
10 |
11 | 一个自动为 Android 项目生成国际化字符串调用的 Gradle 插件。
12 |
13 | [English](README.md) | 简体中文
14 |
15 | | | [BetterAndroid](https://github.com/BetterAndroid) |
16 | |---------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------|
17 |
18 | 这个项目属于上述组织,**点击上方链接关注这个组织**,发现更多好项目。
19 |
20 | ## 这是什么
21 |
22 | 这是一个用来自动为 Android 项目生成国际化字符串调用代码功能的 Gradle 插件。
23 |
24 | 在 Android 项目中,要使用国际化字符串,需要在 `strings.xml` 中进行定义,然后使用 `context.getString(R.string.xxx)` 的方式去调用,非常的繁琐和不灵活。
25 |
26 | 这就是这个项目诞生的原因,通过这个插件,你现在只需要实例化一次插件生成的 `AppLocale` 类,然后就可以在任意地方使用了。
27 |
28 | > 传统写法
29 |
30 | ```kotlin
31 | val appName = context.getString(R.string.app_name)
32 | ```
33 |
34 | > 现代写法
35 |
36 | ```kotlin
37 | val locale by lazy { AppLocale.attach(context) }
38 | val appName = locale.appName
39 | ```
40 |
41 | 如果你依然在使用 Java,那么写法保持不变。
42 |
43 | ```java
44 | var locale = AppLocale.attach(context);
45 | var appName = locale.getAppName();
46 | ```
47 |
48 | ## 兼容性
49 |
50 | 理论支持不是很旧的 Gradle,建议版本为 `7.x.x` 及以上。
51 |
52 | 支持包含 Kotlin 插件的 Android 项目,其它类型的项目暂不支持。
53 |
54 | > 构建脚本语言
55 |
56 | - Kotlin DSL
57 |
58 | 推荐优先使用此语言作为构建脚本语言,这也是目前 Gradle 推荐的语言。
59 |
60 | - Groovy DSL
61 |
62 | 部分功能可能无法兼容,在后期会逐渐放弃支持,且部分功能会无法使用。
63 |
64 | ## 开始使用
65 |
66 | - [点击这里](docs/guide-zh-CN.md) 查看使用文档
67 |
68 | ## 更新日志
69 |
70 | - [点击这里](docs/changelog-zh-CN.md) 查看历史更新日志
71 |
72 | ## 项目推广
73 |
74 |
75 |
76 |
嘿,还请君留步!👋
77 |
这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。
78 |
如果下方的项目能为你提供帮助,不妨为我点个 star 吧!
79 |
所有项目免费、开源,遵循对应开源许可协议。
80 |
81 |
82 |
83 | ## Star History
84 |
85 | 
86 |
87 | ## 许可证
88 |
89 | - [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
90 |
91 | ```
92 | Apache License Version 2.0
93 |
94 | Copyright (C) 2019 HighCapable
95 |
96 | Licensed under the Apache License, Version 2.0 (the "License");
97 | you may not use this file except in compliance with the License.
98 | You may obtain a copy of the License at
99 |
100 | https://www.apache.org/licenses/LICENSE-2.0
101 |
102 | Unless required by applicable law or agreed to in writing, software
103 | distributed under the License is distributed on an "AS IS" BASIS,
104 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105 | See the License for the specific language governing permissions and
106 | limitations under the License.
107 | ```
108 |
109 | 版权所有 © 2019 HighCapable
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flexi Locale
2 |
3 | [](https://github.com/BetterAndroid/FlexiLocale/blob/master/LICENSE)
4 | [](https://github.com/BetterAndroid/FlexiLocale/releases)
5 | [](https://t.me/BetterAndroid)
6 | [](https://t.me/HighCapable_Dev)
7 | [](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf)
8 |
9 |
10 |
11 | An easy generation Android i18ns string call Gradle plugin.
12 |
13 | English | [简体中文](README-zh-CN.md)
14 |
15 | | | [BetterAndroid](https://github.com/BetterAndroid) |
16 | |---------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------|
17 |
18 | This project belongs to the above-mentioned organization, **click the link above to follow this organization** and discover more good projects.
19 |
20 | ## What's this
21 |
22 | This is a Gradle plugin for automatically generating i18ns string calling code functions for Android projects.
23 |
24 | In Android projects, to use i18ns string, you need to define them in `strings.xml` and then call them using `context.getString(R.string.xxx)`, which
25 | is very cumbersome and inflexible.
26 |
27 | That's why this project was born.
28 |
29 | With this plugin, you now only need to instantiate the `AppLocale` class generated by the plugin once, and then you can use it anywhere.
30 |
31 | > Traditional Style
32 |
33 | ```kotlin
34 | val appName = context.getString(R.string.app_name)
35 | ```
36 |
37 | > Modern Style
38 |
39 | ```kotlin
40 | val locale by lazy { AppLocale.attach(context) }
41 | val appName = locale.appName
42 | ```
43 |
44 | If you are still using Java, the writing method remains the same.
45 |
46 | ```java
47 | var locale = AppLocale.attach(context);
48 | var appName = locale.getAppName();
49 | ```
50 |
51 | ## Compatibility
52 |
53 | The theory supports not very old Gradle, the recommended version is `7.x.x` and above.
54 |
55 | Android projects containing Kotlin plugins are supported, other types of projects are not supported yet.
56 |
57 | > Build Script Language
58 |
59 | - Kotlin DSL
60 |
61 | It is recommended to use this language as the build script language first, which is also the language currently recommended by Gradle.
62 |
63 | - Groovy DSL
64 |
65 | Some functions may be incompatible, support will be gradually dropped in the future, and some functions may become unavailable.
66 |
67 | ## Get Started
68 |
69 | - [Click here](docs/guide.md) to view the documentation
70 |
71 | ## Changelog
72 |
73 | - [Click here](docs/changelog.md) to view the historical changelog
74 |
75 | ## Promotion
76 |
77 |
78 |
79 |
Hey, please stay! 👋
80 |
Here are related projects such as Android development tools, UI design, Gradle plugins, Xposed Modules and practical software.
81 |
If the project below can help you, please give me a star!
82 |
All projects are free, open source, and follow the corresponding open source license agreement.
83 |
84 |
85 |
86 | ## Star History
87 |
88 | 
89 |
90 | ## License
91 |
92 | - [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
93 |
94 | ```
95 | Apache License Version 2.0
96 |
97 | Copyright (C) 2019 HighCapable
98 |
99 | Licensed under the Apache License, Version 2.0 (the "License");
100 | you may not use this file except in compliance with the License.
101 | You may obtain a copy of the License at
102 |
103 | https://www.apache.org/licenses/LICENSE-2.0
104 |
105 | Unless required by applicable law or agreed to in writing, software
106 | distributed under the License is distributed on an "AS IS" BASIS,
107 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108 | See the License for the specific language governing permissions and
109 | limitations under the License.
110 | ```
111 |
112 | Copyright © 2019 HighCapable
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | autowire(libs.plugins.kotlin.jvm) apply false
3 | autowire(libs.plugins.maven.publish) apply false
4 | }
--------------------------------------------------------------------------------
/docs/changelog-zh-CN.md:
--------------------------------------------------------------------------------
1 | # 更新日志
2 |
3 | ## 1.0.0 | 2023.10.13
4 |
5 | - 首个版本提交至 Maven
6 |
7 | ## 1.0.1 | 2023.10.13
8 |
9 | - 修复在使用 Kotlin on Android 插件的项目上找不到源码路径的问题
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.0.0 | 2023.10.13
4 |
5 | - The first version is submitted to Maven
6 |
7 | ## 1.0.1 | 2023.10.13
8 |
9 | - Fixed a problem where the source code path could not be found on projects using the Kotlin on Android plugin
--------------------------------------------------------------------------------
/docs/guide-zh-CN.md:
--------------------------------------------------------------------------------
1 | # Flexi Locale 使用文档
2 |
3 | 在开始使用之前,建议你仔细阅读此文档,以便你能更好地了解它的作用方式与功能。
4 |
5 | 如果你的项目依然在使用 Groovy DSL 进行管理,推荐迁移到 Kotlin DSL。
6 |
7 | 在 Groovy DSL 中使用此插件发生的任何问题,我们都将不再负责排查和修复,并且在后期版本可能会完全不再支持 Groovy DSL。
8 |
9 | 注意:此文档中将不再详细介绍在 Groovy DSL 中的使用方法。
10 |
11 | ## 快速开始
12 |
13 | 我们推荐使用 [SweetDependency](https://github.com/HighCapable/SweetDependency) 来自动管理依赖版本。
14 |
15 | 以下是使用 `SweetDependency` 的装载方式。
16 |
17 | > 配置文件
18 |
19 | ```yaml
20 | plugins:
21 | com.highcapable.flexilocale:
22 | alias: flexi-locale
23 | version: +
24 | ```
25 |
26 | > build.gradle.kts
27 |
28 | ```kotlin
29 | plugins {
30 | // 装载方式 1
31 | autowire(libs.plugins.flexi.locale)
32 | // 装载方式 2
33 | autowire("flexi-locale")
34 | }
35 | ```
36 |
37 | 以下是传统的装载方式。
38 |
39 | 打开你需要集成 `FlexiLocale` 插件项目的 `build.gradle.kts`。
40 |
41 | > 示例如下
42 |
43 | ```kotlin
44 | plugins {
45 | id("com.highcapable.flexilocale") version ""
46 | }
47 | ```
48 |
49 | 请将上述代码中的 `` 替换为 [Release](https://github.com/BetterAndroid/FlexiLocale/releases) 中的最新版本, 请注意不要在后方加入 apply false。
50 |
51 | 上述配置完成后,运行一次 Gradle Sync。
52 |
53 | 不出意外的情况下,你将会得到自动生成的 `AppLocale` 类,`Locale` 前方的名称为你的项目名称,默认应该为 `App`。
54 |
55 | ## 功能配置
56 |
57 | 你可以对 `FlexiLocale` 进行配置来实现自定义和个性化功能。
58 |
59 | `FlexiLocale` 为你提供了相对丰富的可自定义功能,下面是这些功能的说明与配置方法。
60 |
61 | 请在你的 `build.gradle.kts` 中添加 `flexiLocale` 方法块以开始配置 `FlexiLocale`。
62 |
63 | `FlexiLocale` 依附于 `android` 方法块生成。
64 |
65 | > 示例如下
66 |
67 | ```kotlin
68 | android {
69 | flexiLocale {
70 | // 启用 FlexiLocale,设置为 false 将禁用所有功能
71 | isEnable = true
72 | // 自定义生成的目录路径
73 | // 你可以填写相对于当前项目的路径
74 | // 默认为 "build/generated/flexi-locale"
75 | // 建议将生成的代码放置于 "build" 目录下,因为生成的代码不建议去修改它
76 | generateDirPath = "build/generated/flexi-locale"
77 | // 自定义生成的包名
78 | // Android 项目默认使用 "android" 配置方法块中的 "namespace"
79 | // 你可以不进行设置,包名在一般情况下会自动进行匹配
80 | packageName = "com.example.mydemo"
81 | // 自定义生成的类名
82 | // 默认使用当前项目的名称 + "Locale"
83 | // 你可以不进行设置,类名在一般情况下会自动进行匹配
84 | className = "MyDemo"
85 | // 是否启用受限访问功能
86 | // 默认不启用,启用后将为生成的类和方法添加 "internal" 修饰符
87 | // 如果你的项目为工具库或依赖,通常情况下建议启用
88 | // 启用后可以防止其他开发者在引用你的库时调用到你的项目国际化字符串调用类发生问题
89 | isEnableRestrictedAccess = false
90 | }
91 | }
92 | ```
93 |
94 | 如需在 Groovy DSL 中使用,请将所有变量的 `=` 改为空格,并删除 `Enable` 前方的 `is` 并将 `E` 小写即可。
95 |
96 | ## 使用示例
97 |
98 | `FlexiLocale` 会自动扫描当前项目 `res/values` 目录中所有包含 `... ` 的 XML 文件。
99 |
100 | 假设这是你当前项目的 `strings.xml`,分为 `default` 和 `zh-rCN` 两个目录。
101 |
102 | > values/strings.xml
103 |
104 | ```xml
105 |
106 | My App
107 | Hello %1$s
108 |
109 | ```
110 |
111 | > values-zh-rCN/strings.xml
112 |
113 | ```xml
114 |
115 | 我的应用
116 | 你好 %1$s
117 |
118 | ```
119 |
120 | 我们建议在 `Application` 中装载 `AppLocale`,这样你就可以在应用的任何地方进行调用。
121 |
122 | > 示例如下
123 |
124 | ```kotlin
125 | lateinit var locale: AppLocale
126 |
127 | class App : Application() {
128 |
129 | override fun onCreate() {
130 | locale = AppLocale.attach(this)
131 | }
132 | }
133 | ```
134 |
135 | 下面,你就可以直接去使用这些字符串了。
136 |
137 | > 示例如下
138 |
139 | ```kotlin
140 | class MainActivity : Activity() {
141 |
142 | override fun onCreate(savedInstanceState: Bundle?) {
143 | // 将设置标题为:
144 | // default: My App
145 | // zh-rCN: 我的应用
146 | actionBar.title = locale.appName
147 | // 将设置文本为:
148 | // default: Hello John
149 | // zh-rCN: 你好 John
150 | findViewById(R.id.hello_text).text = locale.sayHello("John")
151 | }
152 | }
153 | ```
154 |
155 | Java 的使用方式只需要将调用的字符串方法名前加入 `get` 即可。
156 |
157 | 需要注意的是,当你修改了 `strings.xml` 等资源文件,你需要重新运行一次 Gradle Sync 以得到最新的生成结果。
158 |
159 | ### 动态刷新
160 |
161 | 如果用户动态地修改了系统语言,你可以使用 `AppLocale` 中的 `attach { dynamicResources }` 方法设置动态资源实例。
162 |
163 | 如果你是使用 `Context` 装载的 `AppLocale`,那么你无需进行任何操作。
164 |
165 | 请注意,如果 `Activity` 未自动重新启动,请在 `Activity` 中手动调用 `recreate` 才能看到语言改变后的结果。
166 |
167 | ## 问题反馈
168 |
169 | 如果你在使用 `FlexiLocale` 的过程中遇到了任何问题,你都可以随时在 GitHub 开启一个 `issues` 向我们反馈。
--------------------------------------------------------------------------------
/docs/guide.md:
--------------------------------------------------------------------------------
1 | # Flexi Locale Documentation
2 |
3 | Before you start using it, it is recommended that you read this document carefully so that you can better understand how it works and its functions.
4 |
5 | If your project is still managed using Groovy DSL, it is recommended to migrate to Kotlin DSL.
6 |
7 | We will no longer be responsible for troubleshooting and fixing any issues that occur with this plugin in Groovy DSL, and Groovy DSL support may be
8 | dropped entirely in later releases.
9 |
10 | Note: Usage in the Groovy DSL will not be detailed in this document.
11 |
12 | ## Quick Start
13 |
14 | We recommend using [SweetDependency](https://github.com/HighCapable/SweetDependency) to autowire dependencies versions.
15 |
16 | The following is the loading method using `SweetDependency`.
17 |
18 | > Configuration File
19 |
20 | ```yaml
21 | plugins:
22 | com.highcapable.flexilocale:
23 | alias: flexi-locale
24 | version: +
25 | ```
26 |
27 | > build.gradle.kts
28 |
29 | ```kotlin
30 | plugins {
31 | // Loading method 1
32 | autowire(libs.plugins.flexi.locale)
33 | // Loading method 2
34 | autowire("flexi-locale")
35 | }
36 | ```
37 |
38 | The following is the traditional loading method.
39 |
40 | Open the `build.gradle.kts` project where you need to integrate the `FlexiLocale` plugin.
41 |
42 | > The following example
43 |
44 | ```kotlin
45 | plugins {
46 | id("com.highcapable.flexilocale") version ""
47 | }
48 | ```
49 |
50 | Please replace `` in the above code with the latest version in [Release](https://github.com/BetterAndroid/FlexiLocale/releases).
51 |
52 | Please be careful not to add apply false at the end.
53 |
54 | After the above configuration is completed, run Gradle Sync once.
55 |
56 | If nothing else goes wrong, you will get the automatically generated `AppLocale` class,
57 | the name in front of `Locale` is your project name, and the default should be `App`.
58 |
59 | ## Function Configuration
60 |
61 | You can configure `FlexiLocale` to achieve customization and personalization.
62 |
63 | `FlexiLocale` provides you with a relatively rich set of customizable functions.
64 |
65 | The following is the description and configuration method of these functions.
66 |
67 | Please add the `flexiLocale` method block to your `build.gradle.kts` to start configuring `FlexiLocale`.
68 |
69 | `FlexiLocale` depends on the `android` method block generation.
70 |
71 | > The following example
72 |
73 | ```kotlin
74 | android {
75 | flexiLocale {
76 | // Enable FlexiLocale, setting to false will disable all functionality
77 | isEnable = true
78 | // Customize the generated directory path
79 | // You can fill in the path relative to the current project
80 | // Default is "build/generated/flexi-locale"
81 | // It is recommended to place the generated code in the "build" directory, because the generated code is not recommended to be modified
82 | generateDirPath = "build/generated/flexi-locale"
83 | // Customize the generated package name
84 | // Android projects use the "namespace" in the "android" configuration method block by default
85 | // You don't need to set it, the package name will be automatically matched under normal circumstances
86 | packageName = "com.example.mydemo"
87 | // Customize the generated class name
88 | // By default, the name of the current project + "Locale" is used
89 | // You don't need to set it, the class name will be automatically matched under normal circumstances
90 | className = "MyDemo"
91 | // Whether to enable restricted access function
92 | // Not enabled by default. When enabled, the "internal" modifier will be added to the generated classes and methods
93 | // If your project is a tool library or dependency, it is usually recommended to enable it
94 | // Once enabled, it can prevent other developers from calling your project's i18ns string calling classes when they reference your library
95 | isEnableRestrictedAccess = false
96 | }
97 | }
98 | ```
99 |
100 | If you want to use it in Groovy DSL, please change the `=` of all variables to spaces, delete the `is` in front of `Enable` and lowercase `E`.
101 |
102 | ## Usage Example
103 |
104 | `FlexiLocale` will automatically scan all XML files containing `... ` in the `res/values` directory of the current project.
105 |
106 | Assume this is the `strings.xml` of your current project, divided into two directories: `default` and `zh-rCN`.
107 |
108 | > values/strings.xml
109 | ```xml
110 |
111 | My App
112 | Hello %1$s
113 |
114 | ```
115 |
116 | > values-zh-rCN/strings.xml
117 |
118 | ```xml
119 |
120 | 我的应用
121 | 你好 %1$s
122 |
123 | ```
124 |
125 | We recommend loading `AppLocale` in `Application` so you can call it from anywhere in your application.
126 |
127 | > The following example
128 |
129 | ```kotlin
130 | lateinit var locale: AppLocale
131 |
132 | class App : Application() {
133 |
134 | override fun onCreate() {
135 | locale = AppLocale.attach(this)
136 | }
137 | }
138 | ```
139 |
140 | Next, you can use these strings directly.
141 |
142 | > The following example
143 |
144 | ```kotlin
145 | class MainActivity : Activity() {
146 |
147 | override fun onCreate(savedInstanceState: Bundle?) {
148 | // Will set the title to:
149 | // default: My App
150 | // zh-rCN: 我的应用
151 | actionBar.title = locale.appName
152 | // Will set the text to:
153 | // default: Hello John
154 | // zh-rCN: 你好 John
155 | findViewById(R.id.hello_text).text = locale.sayHello("John")
156 | }
157 | }
158 | ```
159 |
160 | To use Java, you only need to add `get` before the name of the string method to be called.
161 |
162 | It should be noted that when you modify resource files such as `strings.xml`, you need to re-run Gradle Sync to get the latest generation results.
163 |
164 | ### Dynamic Refresh
165 |
166 | If the user dynamically changes the system language, you can use the `attach { dynamicResources }` method in `AppLocale` to set a dynamic resources
167 | instance.
168 |
169 | If you loaded `AppLocale` using `Context`, then you don't need to do anything.
170 |
171 | Please note that if the `Activity` is not restarted automatically, please manually call `recreate` in the `Activity` to see the results of the
172 | language change.
173 |
174 | ## Feedback
175 |
176 | If you encounter any problems while using `FlexiLocale`, you can always open an `issues` on GitHub to give us feedback.
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | autowire(libs.plugins.kotlin.jvm)
4 | autowire(libs.plugins.maven.publish)
5 | }
6 |
7 | group = property.project.groupName
8 | version = property.project.version
9 |
10 | java {
11 | sourceCompatibility = JavaVersion.VERSION_21
12 | targetCompatibility = JavaVersion.VERSION_21
13 | withSourcesJar()
14 | }
15 |
16 | kotlin {
17 | jvmToolchain(21)
18 | sourceSets.all { languageSettings { languageVersion = "2.0" } }
19 | compilerOptions {
20 | freeCompilerArgs = listOf(
21 | "-Xno-param-assertions",
22 | "-Xno-call-assertions",
23 | "-Xno-receiver-assertions"
24 | )
25 | }
26 | }
27 |
28 | dependencies {
29 | compileOnly(com.android.library.com.android.library.gradle.plugin)
30 | compileOnly(org.jetbrains.kotlin.kotlin.gradle.plugin)
31 | implementation(com.squareup.kotlinpoet)
32 | }
33 |
34 | gradlePlugin {
35 | plugins {
36 | create(property.project.moduleName) {
37 | id = property.project.groupName
38 | implementationClass = property.gradle.plugin.implementationClass
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/FlexiLocale.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package com.highcapable.flexilocale
25 |
26 | import com.highcapable.flexilocale.generated.FlexiLocaleProperties
27 |
28 | /**
29 | * [FlexiLocale] 的装载调用类
30 | */
31 | object FlexiLocale {
32 |
33 | /** 标签名称 */
34 | const val TAG = FlexiLocaleProperties.PROJECT_NAME
35 |
36 | /** 版本 */
37 | const val VERSION = FlexiLocaleProperties.PROJECT_VERSION
38 |
39 | /** 项目地址 */
40 | const val PROJECT_URL = FlexiLocaleProperties.PROJECT_URL
41 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/gradle/factory/ExtensionAwareFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/11.
21 | */
22 | @file:Suppress("unused", "USELESS_CAST", "KotlinRedundantDiagnosticSuppress")
23 |
24 | package com.highcapable.flexilocale.gradle.factory
25 |
26 | import com.highcapable.flexilocale.utils.debug.FError
27 | import com.highcapable.flexilocale.utils.factory.camelcase
28 | import org.gradle.api.Action
29 | import org.gradle.api.plugins.ExtensionAware
30 |
31 | /**
32 | * 创建、获取扩展方法
33 | * @param name 方法名称 - 自动调用 [toSafeExtName]
34 | * @param clazz 目标对象 [Class]
35 | * @param args 方法参数
36 | * @return [ExtensionAware]
37 | */
38 | internal fun ExtensionAware.getOrCreate(name: String, clazz: Class<*>, vararg args: Any?) = name.toSafeExtName().let { sName ->
39 | runCatching { extensions.create(sName, clazz, *args).asExtension() }.getOrElse {
40 | if (!(it is IllegalArgumentException && it.message?.startsWith("Cannot add extension with name") == true)) throw it
41 | runCatching { extensions.getByName(sName).asExtension() }.getOrNull() ?: FError.make("Create or get extension failed with name \"$sName\"")
42 | }
43 | }
44 |
45 | /**
46 | * 创建、获取扩展方法 - 目标对象 [T]
47 | * @param name 方法名称 - 自动调用 [toSafeExtName]
48 | * @param args 方法参数
49 | * @return [T]
50 | */
51 | internal inline fun ExtensionAware.getOrCreate(name: String, vararg args: Any?) = name.toSafeExtName().let { sName ->
52 | runCatching { extensions.create(sName, T::class.java, *args) as T }.getOrElse {
53 | if (!(it is IllegalArgumentException && it.message?.startsWith("Cannot add extension with name") == true)) throw it
54 | runCatching { extensions.getByName(sName) as? T? }.getOrNull() ?: FError.make("Create or get extension failed with name \"$sName\"")
55 | }
56 | }
57 |
58 | /**
59 | * 获取扩展方法
60 | * @param name 方法名称
61 | * @return [ExtensionAware]
62 | */
63 | internal fun ExtensionAware.get(name: String) =
64 | runCatching { extensions.getByName(name).asExtension() }.getOrNull() ?: FError.make("Could not get extension with name \"$name\"")
65 |
66 | /**
67 | * 获取扩展方法 - 目标对象 [T]
68 | * @param name 方法名称
69 | * @return [T]
70 | */
71 | internal inline fun ExtensionAware.get(name: String) =
72 | runCatching { extensions.getByName(name) as T }.getOrNull() ?: FError.make("Could not get extension with name \"$name\"")
73 |
74 | /**
75 | * 获取扩展方法 - 目标对象 [T]
76 | * @return [T]
77 | */
78 | internal inline fun ExtensionAware.get() =
79 | runCatching { extensions.getByType(T::class.java) as T }.getOrNull() ?: FError.make("Could not get extension with type ${T::class.java}")
80 |
81 | /**
82 | * 配置扩展方法 - 目标对象 [T]
83 | * @param name 方法名称
84 | * @param configure 配置方法体
85 | */
86 | internal inline fun ExtensionAware.configure(name: String, configure: Action) = extensions.configure(name, configure)
87 |
88 | /**
89 | * 是否存在扩展方法
90 | * @param name 方法名称
91 | * @return [Boolean]
92 | */
93 | internal fun ExtensionAware.hasExtension(name: String) = runCatching { extensions.getByName(name); true }.getOrNull() ?: false
94 |
95 | /**
96 | * 转换到扩展方法类型 [ExtensionAware]
97 | * @return [ExtensionAware]
98 | * @throws IllegalStateException 如果类型不是 [ExtensionAware]
99 | */
100 | internal fun Any.asExtension() = this as? ExtensionAware? ?: FError.make("This instance \"$this\" is not a valid Extension")
101 |
102 | /**
103 | * 由于 Gradle 存在一个 [ExtensionAware] 的扩展
104 | *
105 | * 此功能用于检测当前字符串是否为 Gradle 使用的关键字名称
106 | * @return [Boolean]
107 | */
108 | internal fun String.isUnSafeExtName() = camelcase().let { it == "ext" || it == "extra" || it == "extraProperties" || it == "extensions" }
109 |
110 | /**
111 | * 由于 Gradle 存在一个 [ExtensionAware] 的扩展
112 | *
113 | * 此功能用于转换不符合规定的字符串到 "{字符串}s"
114 | * @return [String]
115 | */
116 | internal fun String.toSafeExtName() = if (isUnSafeExtName()) "${this}s" else this
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/gradle/factory/GradleProjectFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/11.
21 | */
22 | package com.highcapable.flexilocale.gradle.factory
23 |
24 | import org.gradle.api.Project
25 |
26 | /**
27 | * 获取指定项目的完整名称 (无子项目前冒号)
28 | * @return [String]
29 | */
30 | internal fun Project.fullName(): String {
31 | val baseNames = mutableListOf()
32 |
33 | /**
34 | * 递归子项目
35 | * @param project 当前项目
36 | */
37 | fun fetchChild(project: Project) {
38 | project.parent?.also { if (it != it.rootProject) fetchChild(it) }
39 | baseNames.add(project.name)
40 | }; fetchChild(project = this)
41 | return buildString { baseNames.onEach { append(":$it") }.clear() }.drop(1)
42 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/gradle/proxy/IProjectLifecycle.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | package com.highcapable.flexilocale.gradle.proxy
23 |
24 | import org.gradle.api.Project
25 |
26 | /**
27 | * Gradle [Project] 生命周期接口
28 | */
29 | internal interface IProjectLifecycle {
30 |
31 | /**
32 | * 当 Gradle 开始装载项目时回调
33 | * @param project 当前项目
34 | */
35 | fun onLoaded(project: Project)
36 |
37 | /**
38 | * 当 Gradle 项目装载完成时回调
39 | * @param project 当前项目
40 | */
41 | fun onEvaluate(project: Project)
42 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/plugin/FlexiLocaleExtension.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | package com.highcapable.flexilocale.plugin
23 |
24 | import com.highcapable.flexilocale.FlexiLocale
25 | import com.highcapable.flexilocale.gradle.factory.get
26 | import com.highcapable.flexilocale.gradle.factory.getOrCreate
27 | import com.highcapable.flexilocale.gradle.proxy.IProjectLifecycle
28 | import com.highcapable.flexilocale.plugin.extension.dsl.configure.FlexiLocaleConfigureExtension
29 | import com.highcapable.flexilocale.plugin.helper.LocaleAnalysisHelper
30 | import com.highcapable.flexilocale.utils.debug.FError
31 | import org.gradle.api.Project
32 |
33 | /**
34 | * [FlexiLocale] 插件扩展类
35 | */
36 | internal class FlexiLocaleExtension internal constructor() : IProjectLifecycle {
37 |
38 | private companion object {
39 |
40 | /** Android Gradle plugin 扩展名称 */
41 | private const val ANDROID_EXTENSION_NAME = "android"
42 | }
43 |
44 | /** 当前配置方法体实例 */
45 | private var configure: FlexiLocaleConfigureExtension? = null
46 |
47 | override fun onLoaded(project: Project) {
48 | runCatching {
49 | configure = project.get(ANDROID_EXTENSION_NAME).getOrCreate(FlexiLocaleConfigureExtension.NAME)
50 | }.onFailure { FError.make("Configure $project got an error, ${FlexiLocale.TAG} can only supports Android projects\nCaused by: $it") }
51 | }
52 |
53 | override fun onEvaluate(project: Project) {
54 | val configs = configure?.build(project) ?: FError.make("Extension \"${FlexiLocaleConfigureExtension.NAME}\" create failed")
55 | LocaleAnalysisHelper.start(project, configs)
56 | }
57 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/plugin/FlexiLocalePlugin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package com.highcapable.flexilocale.plugin
25 |
26 | import com.highcapable.flexilocale.FlexiLocale
27 | import com.highcapable.flexilocale.utils.debug.FError
28 | import org.gradle.api.Plugin
29 | import org.gradle.api.Project
30 | import org.gradle.api.plugins.ExtensionAware
31 |
32 | /**
33 | * [FlexiLocale] 插件定义类
34 | */
35 | class FlexiLocalePlugin internal constructor() : Plugin {
36 |
37 | /** 当前扩展实例 */
38 | private val extension = FlexiLocaleExtension()
39 |
40 | override fun apply(target: T) = when (target) {
41 | is Project -> {
42 | extension.onLoaded(target)
43 | target.afterEvaluate { extension.onEvaluate(project = this) }
44 | }
45 | else -> FError.make("${FlexiLocale.TAG} can only applied in build.gradle or build.gradle.kts, but current is $target")
46 | }
47 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/plugin/config/proxy/IFlexiLocaleConfigs.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/11.
21 | */
22 | package com.highcapable.flexilocale.plugin.config.proxy
23 |
24 | import com.highcapable.flexilocale.FlexiLocale
25 | import com.highcapable.flexilocale.generated.FlexiLocaleProperties
26 |
27 | /**
28 | * [FlexiLocale] 配置类接口类
29 | */
30 | internal interface IFlexiLocaleConfigs {
31 |
32 | companion object {
33 |
34 | /**
35 | * 默认的生成目录路径
36 | *
37 | * "build/generated/[FlexiLocaleProperties.PROJECT_MODULE_NAME]"
38 | */
39 | internal const val DEFAULT_GENERATE_DIR_PATH = "build/generated/${FlexiLocaleProperties.PROJECT_MODULE_NAME}"
40 | }
41 |
42 | /** 是否启用插件 */
43 | val isEnable: Boolean
44 |
45 | /** 自定义生成的目录路径 */
46 | val generateDirPath: String
47 |
48 | /** 自定义生成的包名 */
49 | val packageName: String
50 |
51 | /** 自定义生成的类名 */
52 | val className: String
53 |
54 | /** 是否启用受限访问功能 */
55 | val isEnableRestrictedAccess: Boolean
56 |
57 | /**
58 | * 获取内部 [hashCode]
59 | * @return [Int]
60 | */
61 | fun innerHashCode() = "$isEnable$generateDirPath$packageName$className$isEnableRestrictedAccess".hashCode()
62 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/plugin/extension/dsl/configure/FlexiLocaleConfigureExtension.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/11.
21 | */
22 | @file:Suppress("MemberVisibilityCanBePrivate")
23 |
24 | package com.highcapable.flexilocale.plugin.extension.dsl.configure
25 |
26 | import com.highcapable.flexilocale.FlexiLocale
27 | import com.highcapable.flexilocale.gradle.factory.fullName
28 | import com.highcapable.flexilocale.plugin.config.proxy.IFlexiLocaleConfigs
29 | import com.highcapable.flexilocale.utils.debug.FError
30 | import com.highcapable.flexilocale.utils.factory.uppercamelcase
31 | import org.gradle.api.Project
32 |
33 | /**
34 | * [FlexiLocale] 配置方法体实现类
35 | */
36 | open class FlexiLocaleConfigureExtension internal constructor() {
37 |
38 | internal companion object {
39 |
40 | /** [FlexiLocaleConfigureExtension] 扩展名称 */
41 | internal const val NAME = "flexiLocale"
42 | }
43 |
44 | /**
45 | * 是否启用插件
46 | *
47 | * 默认启用 - 如果你想关闭插件 - 在这里设置就可以了
48 | */
49 | var isEnable = true
50 | @JvmName("enable") set
51 |
52 | /**
53 | * 自定义生成的目录路径
54 | *
55 | * 你可以填写相对于当前项目的路径
56 | *
57 | * 默认为 [IFlexiLocaleConfigs.DEFAULT_GENERATE_DIR_PATH]
58 | */
59 | var generateDirPath = IFlexiLocaleConfigs.DEFAULT_GENERATE_DIR_PATH
60 | @JvmName("generateDirPath") set
61 |
62 | /**
63 | * 自定义生成的包名
64 | *
65 | * Android 项目默认使用 "android" 配置方法块中的 "namespace"
66 | */
67 | var packageName = ""
68 | @JvmName("packageName") set
69 |
70 | /**
71 | * 自定义生成的类名
72 | *
73 | * 默认使用当前项目的名称 + "Locale"
74 | */
75 | var className = ""
76 | @JvmName("className") set
77 |
78 | /**
79 | * 是否启用受限访问功能
80 | *
81 | * 默认不启用 - 启用后将为生成的类和方法添加 "internal" 修饰符
82 | */
83 | var isEnableRestrictedAccess = false
84 | @JvmName("enableRestrictedAccess") set
85 |
86 | /**
87 | * 构造 [IFlexiLocaleConfigs]
88 | * @param project 当前项目
89 | * @return [IFlexiLocaleConfigs]
90 | */
91 | internal fun build(project: Project): IFlexiLocaleConfigs {
92 | /** 检查合法包名 */
93 | fun String.checkingValidPackageName() {
94 | if (isNotBlank() && !matches("^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*$".toRegex()))
95 | FError.make("Invalid package name \"$this\"")
96 | }
97 |
98 | /** 检查合法类名 */
99 | fun String.checkingValidClassName() {
100 | if (isNotBlank() && !matches("^[a-zA-Z][a-zA-Z0-9_]*$".toRegex()))
101 | FError.make("Invalid class name \"$this\"")
102 | }
103 | packageName.checkingValidPackageName()
104 | className.checkingValidClassName()
105 | val currentEnable = isEnable
106 | val currentGenerateDirPath = project.file(generateDirPath).absolutePath
107 | val currentPackageName = packageName
108 | val currentClassName = "${className.ifBlank { project.fullName().uppercamelcase() }}Locale"
109 | val currentEnableRestrictedAccess = isEnableRestrictedAccess
110 | return object : IFlexiLocaleConfigs {
111 | override val isEnable get() = currentEnable
112 | override val generateDirPath get() = currentGenerateDirPath
113 | override val packageName get() = currentPackageName
114 | override val className get() = currentClassName
115 | override val isEnableRestrictedAccess get() = currentEnableRestrictedAccess
116 | }
117 | }
118 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/plugin/generator/LocaleSourcesGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | package com.highcapable.flexilocale.plugin.generator
23 |
24 | import com.highcapable.flexilocale.FlexiLocale
25 | import com.highcapable.flexilocale.plugin.config.proxy.IFlexiLocaleConfigs
26 | import com.highcapable.flexilocale.plugin.generator.factory.LocaleStringMap
27 | import com.highcapable.flexilocale.utils.debug.FError
28 | import com.highcapable.flexilocale.utils.factory.camelcase
29 | import com.highcapable.flexilocale.utils.factory.uppercamelcase
30 | import com.squareup.kotlinpoet.AnnotationSpec
31 | import com.squareup.kotlinpoet.ClassName
32 | import com.squareup.kotlinpoet.FileSpec
33 | import com.squareup.kotlinpoet.FunSpec
34 | import com.squareup.kotlinpoet.KModifier
35 | import com.squareup.kotlinpoet.LambdaTypeName
36 | import com.squareup.kotlinpoet.ParameterSpec
37 | import com.squareup.kotlinpoet.PropertySpec
38 | import com.squareup.kotlinpoet.TypeSpec
39 | import com.squareup.kotlinpoet.asTypeName
40 | import java.text.SimpleDateFormat
41 | import java.util.*
42 | import kotlin.math.abs
43 |
44 | /**
45 | * I18ns 生成工具类
46 | */
47 | internal class LocaleSourcesGenerator {
48 |
49 | /**
50 | * 生成 [FileSpec]
51 | * @param configs 当前配置
52 | * @param keyValues 键值数组
53 | * @param namespace 命名空间
54 | * @param packageName 包名
55 | * @return [FileSpec]
56 | * @throws IllegalStateException 如果生成失败
57 | */
58 | internal fun build(
59 | configs: IFlexiLocaleConfigs,
60 | keyValues: LocaleStringMap,
61 | namespace: String,
62 | packageName: String
63 | ) = runCatching {
64 | FileSpec.builder(packageName, configs.className).apply {
65 | val selfClass = ClassName(packageName, configs.className)
66 | val contextClass = ClassName("android.content", "Context")
67 | val resourcesClass = ClassName("android.content.res", "Resources")
68 | val resourcesInitializer = LambdaTypeName.get(returnType = resourcesClass, parameters = emptyList())
69 | addAnnotation(AnnotationSpec.builder(Suppress::class).addMember("\"StringFormatInvalid\"").build())
70 | addImport(namespace, "R")
71 | addType(TypeSpec.classBuilder(selfClass).apply {
72 | addKdoc(
73 | """
74 | This class is generated by ${FlexiLocale.TAG} at ${SimpleDateFormat.getDateTimeInstance().format(Date())}
75 |
76 | The content here is automatically generated according to the res/values of your projects
77 |
78 | You can visit [here](${FlexiLocale.PROJECT_URL}) for more help
79 | """.trimIndent()
80 | )
81 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
82 | addFunction(FunSpec.constructorBuilder().addModifiers(KModifier.PRIVATE).build())
83 | addProperty(PropertySpec.builder("context", contextClass.copy(nullable = true)).apply {
84 | addKdoc("The current [Context] for this app or library")
85 | addModifiers(KModifier.PRIVATE)
86 | mutable()
87 | initializer("null")
88 | }.build())
89 | addProperty(PropertySpec.builder("resources", resourcesClass.copy(nullable = true)).apply {
90 | addKdoc("The current [Resources] for this app or library")
91 | addModifiers(KModifier.PRIVATE)
92 | mutable()
93 | initializer("null")
94 | }.build())
95 | addProperty(PropertySpec.builder("resourcesInitializer", resourcesInitializer.copy(nullable = true)).apply {
96 | addKdoc("The current [Resources] initializer for this app or library")
97 | addModifiers(KModifier.PRIVATE)
98 | mutable()
99 | initializer("null")
100 | }.build())
101 | addType(TypeSpec.companionObjectBuilder().apply {
102 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
103 | addFunction(FunSpec.builder("attach").apply {
104 | addKdoc(
105 | """
106 | Attach [${selfClass.simpleName}] to [Context]
107 | @param context like [android.app.Application] or [android.app.Activity]
108 | @return [${selfClass.simpleName}]
109 | """.trimIndent()
110 | )
111 | addAnnotation(JvmStatic::class)
112 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
113 | addParameter("context", contextClass)
114 | addStatement("return ${selfClass.simpleName}().apply { this.context = context }")
115 | returns(selfClass)
116 | }.build())
117 | addFunction(FunSpec.builder("attach").apply {
118 | addKdoc(
119 | """
120 | Attach [${selfClass.simpleName}] to [Resources]
121 |
122 | - Note: this method will have no effect if [context] already exists
123 | @param resources A [Resources] that exists and has not been recycled
124 | @return [${selfClass.simpleName}]
125 | """.trimIndent()
126 | )
127 | addAnnotation(JvmStatic::class)
128 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
129 | addParameter("resources", resourcesClass)
130 | addStatement("return ${selfClass.simpleName}().apply { this.resources = resources }")
131 | returns(selfClass)
132 | }.build())
133 | addFunction(FunSpec.builder("attach").apply {
134 | addKdoc(
135 | """
136 | Attach [${selfClass.simpleName}] to [Resources] initializer
137 |
138 | - Note: this method will have no effect if [context] already exists
139 | @param resourcesInitializer A [Resources] initializer returns a non-recycled instance
140 | @return [${selfClass.simpleName}]
141 | """.trimIndent()
142 | )
143 | addAnnotation(JvmStatic::class)
144 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
145 | addParameter("resourcesInitializer", resourcesInitializer)
146 | addStatement("return ${selfClass.simpleName}().apply { this.resourcesInitializer = resourcesInitializer }")
147 | returns(selfClass)
148 | }.build())
149 | }.build())
150 | addProperty(PropertySpec.builder("currentResources", resourcesClass).apply {
151 | addKdoc("The current used [Resources] for this app or library")
152 | addModifiers(KModifier.PRIVATE)
153 | getter(FunSpec.getterBuilder().apply {
154 | addStatement("return context?.resources ?: resourcesInitializer?.invoke() ?: resources" +
155 | "?: error(\"${("Unable to get Resource instance, the app may have been killed " +
156 | "or initialization process failed").toKotlinPoetSpace()}\")")
157 | }.build())
158 | }.build())
159 | keyValues.forEach { (key, contentValues) ->
160 | val fixedKey = key.camelcase()
161 | val getterKey = "get${key.uppercamelcase()}"
162 | val statement = "return currentResources.getString(R.string.$key, *formatArgs)"
163 | var kDoc = "Resolve the [R.string.$key]\n\n"
164 | if (contentValues.isNotEmpty()) kDoc += "| Configuration | Value |\n| --- | --- |\n"
165 | contentValues.toList()
166 | .sortedWith(compareBy> { it.first != "default" }.thenBy { it.first })
167 | .toAutoWrapKeyValues()
168 | .forEach { (key, value) ->
169 | val displayValue = value.replace("%".toRegex(), "%%")
170 | kDoc += "| $key | $displayValue |\n"
171 | }; kDoc = kDoc.trim()
172 | addProperty(PropertySpec.builder(fixedKey, String::class).apply {
173 | addKdoc(kDoc)
174 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
175 | getter(FunSpec.getterBuilder().apply {
176 | addAnnotation(AnnotationSpec.builder(JvmName::class).addMember("\"$getterKey\"").build())
177 | addStatement("return $fixedKey()")
178 | }.build())
179 | }.build())
180 | addFunction(FunSpec.builder(fixedKey).apply {
181 | addKdoc("$kDoc\n@param formatArgs The format arguments that will be used for substitution")
182 | addAnnotation(AnnotationSpec.builder(JvmName::class).addMember("\"$getterKey\"").build())
183 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
184 | addParameter(ParameterSpec.builder("formatArgs", Any::class.asTypeName()).addModifiers(KModifier.VARARG).build())
185 | addStatement(statement)
186 | returns(String::class)
187 | }.build())
188 | }
189 | }.build())
190 | }.build()
191 | }.getOrElse { FError.make("Failed to generated Kotlin file\n$it") }
192 |
193 | /**
194 | * 转换为自动换行键值对数组
195 | * @return [List]<[Pair]<[String], [String]>>
196 | */
197 | private fun List>.toAutoWrapKeyValues(): List> {
198 | val maxAllowLength = 75
199 | val punctuations = charArrayOf('.', '。', ',', ',', '、', ';', ';', ':', ':', '!', '!', '?', '?')
200 | val result = mutableListOf>()
201 | val placeholders = mutableListOf>()
202 | forEach {
203 | var key = it.first
204 | var value = it.second.replace("\\n", "ㅤ")
205 | val maxLength = abs(maxAllowLength - key.length)
206 | while (value.length > maxLength) {
207 | var splitIndex = maxLength
208 | var splitValue = value.substring(0, splitIndex)
209 | val lastSpaceIndex = splitValue.lastIndexOf(' ')
210 | val lastPunctuationIndex = splitValue.lastIndexOfAny(punctuations)
211 | val hashWrapIndex = splitValue.lastIndexOf('ㅤ')
212 | when {
213 | hashWrapIndex != -1 && (hashWrapIndex < lastSpaceIndex || hashWrapIndex < lastPunctuationIndex) -> {
214 | splitIndex = hashWrapIndex
215 | splitValue = value.substring(0, splitIndex)
216 | }
217 | lastSpaceIndex != -1 && lastSpaceIndex >= lastPunctuationIndex -> {
218 | splitIndex = lastSpaceIndex + 1
219 | splitValue = value.substring(0, splitIndex)
220 | }
221 | lastPunctuationIndex != -1 -> {
222 | splitIndex = lastPunctuationIndex + 1
223 | splitValue = value.substring(0, splitIndex)
224 | }
225 | }
226 | value = value.substring(splitIndex).trimStart('ㅤ')
227 | result.add(key to splitValue)
228 | key = " ".repeat(key.length)
229 | }
230 | if (value.isNotEmpty())
231 | result.add(key to value.replace("ㅤ", ""))
232 | else placeholders.add(key to "")
233 | }; result.addAll(placeholders)
234 | return result
235 | }
236 |
237 | /**
238 | * 转换到 KotlinPoet 声明的空格
239 | * @return [String]
240 | */
241 | private fun String.toKotlinPoetSpace() = replace(" ", "·")
242 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/plugin/generator/factory/GeneratorFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/13.
21 | */
22 | package com.highcapable.flexilocale.plugin.generator.factory
23 |
24 | import java.io.File
25 |
26 | /** I18ns 数组类型定义 */
27 | internal typealias LocaleStringMap = MutableMap
28 |
29 | /** I18ns (子键值对) 数组类型定义 */
30 | internal typealias LocaleChildMap = MutableMap
31 |
32 | /** I18ns (文件) 数组类型定义 */
33 | internal typealias LocaleFileMap = MutableMap>
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/plugin/helper/LocaleAnalysisHelper.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | @file:Suppress("DEPRECATION")
23 |
24 | package com.highcapable.flexilocale.plugin.helper
25 |
26 | import com.android.build.gradle.AppExtension
27 | import com.android.build.gradle.BaseExtension
28 | import com.android.build.gradle.LibraryExtension
29 | import com.android.build.gradle.api.BaseVariant
30 | import com.highcapable.flexilocale.gradle.factory.get
31 | import com.highcapable.flexilocale.plugin.config.proxy.IFlexiLocaleConfigs
32 | import com.highcapable.flexilocale.plugin.generator.LocaleSourcesGenerator
33 | import com.highcapable.flexilocale.plugin.generator.factory.LocaleChildMap
34 | import com.highcapable.flexilocale.plugin.generator.factory.LocaleFileMap
35 | import com.highcapable.flexilocale.plugin.generator.factory.LocaleStringMap
36 | import com.highcapable.flexilocale.utils.debug.FError
37 | import com.highcapable.flexilocale.utils.debug.FLog
38 | import com.highcapable.flexilocale.utils.factory.toFile
39 | import org.gradle.api.Project
40 | import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
41 | import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
42 | import org.w3c.dom.Element
43 | import org.w3c.dom.Node
44 | import java.io.File
45 | import javax.xml.parsers.DocumentBuilderFactory
46 |
47 | /**
48 | * I18ns 分析工具类
49 | */
50 | internal object LocaleAnalysisHelper {
51 |
52 | /** Android 的 Application 插件名称 */
53 | private const val APPLICATION_PLUGIN_NAME = "com.android.application"
54 |
55 | /** Android 的 Library 插件名称 */
56 | private const val LIBRARY_PLUGIN_NAME = "com.android.library"
57 |
58 | /** Kotlin 的 Android 插件名称 */
59 | private const val KT_ANDROID_PLUGIN_NAME = "org.jetbrains.kotlin.android"
60 |
61 | /** I18ns 代码生成实例 */
62 | private val generator = LocaleSourcesGenerator()
63 |
64 | /** 当前全部 I18ns 数据 (来自但不一定完全为 strings.xml) */
65 | private val mappedStrings: LocaleStringMap = mutableMapOf()
66 |
67 | /** 当前项目命名空间 */
68 | private var namespace = ""
69 |
70 | /** 当前项目资源目录数组 */
71 | private val resDirectories = mutableListOf()
72 |
73 | /** 上次修改的 Hash Code */
74 | private var lastModifiedHashCode = 0
75 |
76 | /** 配置是否已被修改 */
77 | private var isConfigsModified = true
78 |
79 | /** 当前使用的配置实例 */
80 | private lateinit var configs: IFlexiLocaleConfigs
81 |
82 | /**
83 | * 开始分析当前项目
84 | * @param project 当前项目
85 | * @param configs 当前配置
86 | */
87 | internal fun start(project: Project, configs: IFlexiLocaleConfigs) {
88 | this.configs = configs
89 | if (!configs.isEnable) return
90 | checkingConfigsModified(project, configs)
91 | initializePlugins(project)
92 | val lastMappedStrings: LocaleStringMap = mutableMapOf()
93 | val lastResolveStrings: LocaleStringMap = mutableMapOf()
94 | resDirectories.takeIf { it.isNotEmpty() }?.allValuesDirs()?.forEach { (localeName, files) ->
95 | val stringXmls: LocaleChildMap = mutableMapOf()
96 | files.forEach { stringXmls.putAll(resolveStringXml(it)) }
97 | lastResolveStrings[localeName] = stringXmls
98 | } ?: return FLog.warn(
99 | "Unable to get the resources dir of $project, " +
100 | "please check whether there does not have a resources dir or is not an Android project"
101 | )
102 | lastResolveStrings.onEach { (localeName, strings) ->
103 | strings.forEach { (key, value) ->
104 | if (lastMappedStrings[key] == null) lastMappedStrings[key] = mutableMapOf()
105 | lastMappedStrings[key]?.set(localeName, value)
106 | }
107 | }.clear()
108 | val isFileModified = mappedStrings != lastMappedStrings
109 | if (!isFileModified && !isConfigsModified) return
110 | mappedStrings.clear()
111 | mappedStrings.putAll(lastMappedStrings)
112 | lastMappedStrings.clear()
113 | updateGeneration()
114 | }
115 |
116 | /**
117 | * 检查配置是否已被修改
118 | * @param project 当前项目
119 | * @param configs 当前配置
120 | */
121 | private fun checkingConfigsModified(project: Project, configs: IFlexiLocaleConfigs) {
122 | val fileHashCode = project.buildFile.takeIf { it.exists() }?.readText()?.hashCode() ?: -1
123 | isConfigsModified = fileHashCode == -1 || lastModifiedHashCode != fileHashCode || this.configs.innerHashCode() != configs.innerHashCode()
124 | lastModifiedHashCode = fileHashCode
125 | }
126 |
127 | /**
128 | * 初始化 Android Gradle plugin
129 | * @param project 当前项目
130 | */
131 | private fun initializePlugins(project: Project) {
132 | runCatching {
133 | fun BaseExtension.updateSourceDirs() = sourceSets.configureEach { kotlin.srcDir(configs.generateDirPath) }
134 | fun KotlinProjectExtension.updateSourceDirs() = sourceSets.configureEach { kotlin.srcDir(configs.generateDirPath) }
135 | fun BaseVariant.updateResDirectories() = sourceSets.forEach { provide -> provide.resDirectories?.also { resDirectories.addAll(it) } }
136 | project.plugins.withId(APPLICATION_PLUGIN_NAME) {
137 | project.get().also { extension ->
138 | namespace = extension.namespace ?: ""
139 | extension.applicationVariants.forEach { variant ->
140 | variant.updateResDirectories()
141 | }; extension.updateSourceDirs()
142 | }
143 | }
144 | project.plugins.withId(LIBRARY_PLUGIN_NAME) {
145 | project.get().also { extension ->
146 | namespace = extension.namespace ?: ""
147 | extension.libraryVariants.forEach { variant ->
148 | variant.updateResDirectories()
149 | }; extension.updateSourceDirs()
150 | }
151 | }
152 | project.plugins.withId(KT_ANDROID_PLUGIN_NAME) {
153 | project.get().also { extension ->
154 | extension.updateSourceDirs()
155 | }
156 | }
157 | }.onFailure { FError.make("Failed to initialize Android Gradle plugin, this may be not or a wrong Android project\n$it") }
158 | }
159 |
160 | /** 更新生成后的代码内容 */
161 | private fun updateGeneration() {
162 | val packageName = "${configs.packageName.ifBlank { namespace }}.generated.locale"
163 | val generateDir = configs.generateDirPath.toFile().apply { if (exists() && isDirectory) deleteRecursively() }
164 | generator.build(configs, mappedStrings, namespace, packageName).writeTo(generateDir)
165 | }
166 |
167 | /**
168 | * 解析当前资源目录下的全部可用 values 目录数组 (包含 I18ns 数据)
169 | * @return [LocaleFileMap]
170 | */
171 | private fun List.allValuesDirs(): LocaleFileMap {
172 | val valuesDirs: LocaleFileMap = mutableMapOf()
173 | forEach {
174 | it.listFiles()?.filter { dir -> dir.name.startsWith("values") }?.forEach eachDir@{ valuesDir ->
175 | if (!valuesDir.exists() || !valuesDir.isDirectory) return@eachDir
176 | val langName = if (valuesDir.name == "values") "default" else valuesDir.name.split("s-").getOrNull(1) ?: return@eachDir
177 | if (valuesDirs[langName] == null) valuesDirs[langName] = mutableSetOf()
178 | valuesDirs[langName]?.add(valuesDir)
179 | }
180 | }; return valuesDirs
181 | }
182 |
183 | /**
184 | * 解析当前资源目录下的全部 Xml 文件内容到键值对数组
185 | * @param valuesDir 当前资源目录
186 | * @return [LocaleChildMap]
187 | */
188 | private fun resolveStringXml(valuesDir: File): LocaleChildMap {
189 | val lastMappedStrings: LocaleChildMap = mutableMapOf()
190 | valuesDir.listFiles()?.filter { it.name.endsWith(".xml") }?.forEach {
191 | lastMappedStrings.putAll(it.readText().parseResourcesXml())
192 | }; return lastMappedStrings
193 | }
194 |
195 | /**
196 | * 解析资源 Xml 文件内容到键值对数组
197 | * @return [LocaleChildMap]
198 | */
199 | private fun String.parseResourcesXml(): LocaleChildMap {
200 | val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
201 | val document = runCatching { builder.parse(byteInputStream()) }.getOrNull() ?: return mutableMapOf()
202 | val rootNode = document.documentElement
203 | if (rootNode.nodeName != "resources") return mutableMapOf()
204 | val nodes = rootNode.getElementsByTagName("string")
205 | val keyValues: LocaleChildMap = mutableMapOf()
206 | (0 until nodes.length).forEach { index ->
207 | val node = nodes.item(index)
208 | if (node.nodeType == Node.ELEMENT_NODE) {
209 | val element = node as Element
210 | val name = element.getAttribute("name")
211 | val content = element.textContent
212 | keyValues[name] = content
213 | }
214 | }; return keyValues
215 | }
216 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/utils/debug/FError.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | package com.highcapable.flexilocale.utils.debug
23 |
24 | import com.highcapable.flexilocale.FlexiLocale
25 |
26 | /**
27 | * 全局异常管理类
28 | */
29 | internal object FError {
30 |
31 | /**
32 | * 抛出异常
33 | * @param msg 消息内容
34 | * @throws IllegalStateException
35 | */
36 | internal fun make(msg: String): Nothing = error("[${FlexiLocale.TAG}] $msg")
37 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/utils/debug/FLog.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | @file:Suppress("unused", "MemberVisibilityCanBePrivate")
23 |
24 | package com.highcapable.flexilocale.utils.debug
25 |
26 | import com.highcapable.flexilocale.FlexiLocale
27 | import org.apache.log4j.Logger
28 |
29 | /**
30 | * 全局 Log 管理类
31 | */
32 | internal object FLog {
33 |
34 | internal const val DONE = "✅"
35 | internal const val IGNORE = "❎"
36 | internal const val ERROR = "❌"
37 | internal const val WARN = "⚠️"
38 | internal const val LINK = "➡️"
39 | internal const val WIRE = "⚙️"
40 | internal const val UP = "⬆️"
41 | internal const val ROTATE = "\uD83D\uDD04"
42 | internal const val ANLZE = "\uD83D\uDD0D"
43 | internal const val STRNG = "\uD83D\uDCAA"
44 |
45 | /** 当前日志输出对象 */
46 | private val logger = Logger.getLogger(FLog::class.java)
47 |
48 | /**
49 | * 打印 Info (提醒) 级别 Log (绿色)
50 | * @param msg 消息内容
51 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认无
52 | * @param noTag 无标签 - 默认否
53 | */
54 | internal fun note(msg: Any, symbol: String = "", noTag: Boolean = false) =
55 | log(if (noTag) msg else msg.createSymbolMsg(symbol), color = "38;5;10")
56 |
57 | /**
58 | * 打印 Info 级别 Log (无颜色)
59 | * @param msg 消息内容
60 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认无
61 | * @param noTag 无标签 - 默认否
62 | */
63 | internal fun info(msg: Any, symbol: String = "", noTag: Boolean = false) =
64 | log(if (noTag) msg else msg.createSymbolMsg(symbol))
65 |
66 | /**
67 | * 打印 Warn 级别 Log (黄色)
68 | * @param msg 消息内容
69 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认 [WARN]
70 | * @param noTag 无标签 - 默认否
71 | */
72 | internal fun warn(msg: Any, symbol: String = WARN, noTag: Boolean = false) =
73 | log(if (noTag) msg else msg.createSymbolMsg(symbol), color = "33")
74 |
75 | /**
76 | * 打印 Error 级别 Log (红色)
77 | * @param msg 消息内容
78 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认 [ERROR]
79 | * @param noTag 无标签 - 默认否
80 | */
81 | internal fun error(msg: Any, symbol: String = ERROR, noTag: Boolean = false) =
82 | log(if (noTag) msg else msg.createSymbolMsg(symbol), isError = true)
83 |
84 | /**
85 | * 创建符号消息内容
86 | * @param symbol 前缀符号
87 | * @return [String]
88 | */
89 | private fun Any.createSymbolMsg(symbol: String) =
90 | if (symbol.isNotBlank()) "[${FlexiLocale.TAG}] $symbol $this" else "[${FlexiLocale.TAG}] $this"
91 |
92 | /**
93 | * 打印 Log
94 | * @param msg 消息内容
95 | * @param color 颜色代码 - 默认无颜色
96 | * @param isError 是否强制为错误日志 - 默认否
97 | */
98 | private fun log(msg: Any, color: String = "0", isError: Boolean = false) = when {
99 | isError -> logger.error(msg)
100 | color != "0" -> println("\u001B[${color}m$msg\u001B[0m")
101 | else -> println(msg)
102 | }
103 | }
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/utils/factory/FileFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/11.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package com.highcapable.flexilocale.utils.factory
25 |
26 | import java.io.File
27 |
28 | /**
29 | * 字符串路径转换为文件
30 | *
31 | * 自动调用 [parseFileSeparator]
32 | * @return [File]
33 | */
34 | internal fun String.toFile() = File(parseFileSeparator())
35 |
36 | /**
37 | * 格式化到当前操作系统的文件分隔符
38 | * @return [String]
39 | */
40 | internal fun String.parseFileSeparator() = replace("/", File.separator).replace("\\", File.separator)
--------------------------------------------------------------------------------
/flexilocale-gradle-plugin/src/main/kotlin/com/highcapable/flexilocale/utils/factory/VariableFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * FlexiLocale - An easy generation Android i18ns string call Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/BetterAndroid/FlexiLocale
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/10/10.
21 | */
22 | package com.highcapable.flexilocale.utils.factory
23 |
24 | /**
25 | * 下划线、分隔线、点、冒号、空格命名字符串转小驼峰命名字符串
26 | * @return [String]
27 | */
28 | internal fun String.camelcase() = runCatching {
29 | split("_", ".", "-", ":", " ").map { it.replaceFirstChar { e -> e.titlecase() } }.let { words ->
30 | words.first().replaceFirstChar { it.lowercase() } + words.drop(1).joinToString("")
31 | }
32 | }.getOrNull() ?: this
33 |
34 | /**
35 | * 下划线、分隔线、点、空格命名字符串转大驼峰命名字符串
36 | * @return [String]
37 | */
38 | internal fun String.uppercamelcase() = camelcase().capitalize()
39 |
40 | /**
41 | * 字符串首字母大写
42 | * @return [String]
43 | */
44 | internal fun String.capitalize() = replaceFirstChar { it.uppercaseChar() }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project Configuration
2 | project.name=FlexiLocale
3 | project.url=https://github.com/BetterAndroid/FlexiLocale
4 | project.groupName=com.highcapable.flexilocale
5 | project.moduleName=flexi-locale
6 | project.version=1.0.1
7 | # Gradle Plugin Configuration
8 | gradle.plugin.moduleName=${project.groupName}.gradle.plugin
9 | gradle.plugin.implementationClass=${project.groupName}.plugin.FlexiLocalePlugin
10 | # Maven Publish Configuration
11 | SONATYPE_HOST=S01
12 | RELEASE_SIGNING_ENABLED=true
13 | # Maven POM Configuration
14 | POM_NAME=FlexiLocale
15 | POM_ARTIFACT_ID=flexi-locale
16 | POM_DESCRIPTION=An easy generation Android i18ns string call Gradle plugin.
17 | POM_URL=https://github.com/BetterAndroid/FlexiLocale
18 | POM_LICENSE_NAME=Apache License 2.0
19 | POM_LICENSE_URL=https://github.com/BetterAndroid/FlexiLocale/blob/master/LICENSE
20 | POM_LICENSE_DIST=repo
21 | POM_SCM_URL=https://github.com/BetterAndroid/FlexiLocale
22 | POM_SCM_CONNECTION=scm:git:git://github.com/BetterAndroid/FlexiLocale.git
23 | POM_SCM_DEV_CONNECTION=scm:git:ssh://github.com/BetterAndroid/FlexiLocale.git
24 | POM_DEVELOPER_ID=0
25 | POM_DEVELOPER_NAME=fankes
26 | POM_DEVELOPER_EMAIL=qzmmcn@163.com
27 | POM_DEVELOPER_URL=https://github.com/fankes
--------------------------------------------------------------------------------
/gradle/sweet-dependency/sweet-dependency-config.yaml:
--------------------------------------------------------------------------------
1 | preferences:
2 | autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES
3 | repositories-mode: FAIL_ON_PROJECT_REPOS
4 |
5 | repositories:
6 | gradle-plugin-portal:
7 | scope: PLUGINS
8 | google:
9 | maven-central:
10 |
11 | plugins:
12 | org.jetbrains.kotlin.jvm:
13 | alias: kotlin-jvm
14 | version: 2.1.10
15 | com.vanniktech.maven.publish:
16 | alias: maven-publish
17 | version: 0.31.0
18 |
19 | libraries:
20 | com.android.library:
21 | com.android.library.gradle.plugin:
22 | version: 8.9.0
23 | org.jetbrains.kotlin:
24 | kotlin-gradle-plugin:
25 | version: 2.1.10
26 | com.squareup:
27 | kotlinpoet:
28 | version: 2.1.0
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BetterAndroid/FlexiLocale/56ff6a109aaecb851b09c82b6f4170a3c33fad10/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/img-src/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BetterAndroid/FlexiLocale/56ff6a109aaecb851b09c82b6f4170a3c33fad10/img-src/icon.png
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | plugins {
9 | id("com.highcapable.sweetdependency") version "1.0.4"
10 | id("com.highcapable.sweetproperty") version "1.0.5"
11 | }
12 | sweetDependency {
13 | isEnableVerboseMode = false
14 | }
15 | sweetProperty {
16 | global {
17 | sourcesCode {
18 | className = rootProject.name
19 | includeKeys(
20 | "^project\\..*\$".toRegex(),
21 | "^gradle\\..*\$".toRegex()
22 | )
23 | isEnableRestrictedAccess = true
24 | }
25 | }
26 | rootProject { all { isEnable = false } }
27 | }
28 | rootProject.name = "FlexiLocale"
29 | include(":flexilocale-gradle-plugin")
--------------------------------------------------------------------------------