Skip to content

Commit 97cecd8

Browse files
committed
* fix issue #30
* add string key inspection
1 parent ead99a9 commit 97cecd8

File tree

5 files changed

+77
-57
lines changed

5 files changed

+77
-57
lines changed

README.md

+19-3
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ Flutter internalization only depends on a small subset of the ARB format. Each .
7575

7676
<b>The first English file is generated for you(/res/values/strings_en.arb).</b> Every arb file depends on this one. If you have an a string in the German arb file(/res/values/strings_de.arb) that has an ID that is <b>not found</b> in the English file, it would not be listed. So you must be sure to first have the strings in the English file and then add other translations.
7777

78-
Do not add white spaces or illegal characters in your IDs. They should match this RegEx <b>[a-zA-Z]</b> <span style="color: rgb(0, 0, 0); font-family: &quot;Source Sans Pro&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">for now</span>. The IDE doesn't worn you if your ID doesn't match.
79-
8078
To add a new arb file right click on <b>values</b> folder and select <b>New</b> -><b> Arb </b><b>File</b>. Then pick your language from the list, and region if necessary.
8179

8280
#### 1. Referencing the values
@@ -105,7 +103,7 @@ The value for this resource ID is retrieved with a parametrized method instead o
105103

106104
#### 3. Plurals
107105

108-
Plural translations can be provided for several quantities: 0, 1, 2, "few", "many", "other". The variations are identified by a resource ID suffix which must be one of "Zero", "One", "Two", "Few", "Many", "Other". The "Other" variation is used when none of the other quantities apply. All plural resources must include a resource with the "Other" suffix. For example the English translations ('material_en.arb') for selectedRowCountTitle in the [Material Library Localizations](https://github.com/flutter/flutter/tree/master/packages/flutter_localizations/lib/src/l10n) are:
106+
Plural translations can be provided for several quantities: 0, 1, 2, "few", "many", "other". The variations are identified by a resource ID suffix which must be one of "Zero", "One", "Two", "Few", "Many", "Other" (case insensitive). The "Other" variation is used when none of the other quantities apply. All plural resources must include a resource with the "Other" suffix. For example the English translations ('material_en.arb') for selectedRowCountTitle in the [Material Library Localizations](https://github.com/flutter/flutter/tree/master/packages/flutter_localizations/lib/src/l10n) are:
109107

110108
{
111109
"selectedRowCountTitleZero": "No items selected",
@@ -126,6 +124,24 @@ or
126124

127125
<pre>S.of(context).selectedRowCountTitle("$selectedRowCount")</pre>
128126

127+
### NOTES:
128+
* The plugin also supports `${variable}` notation. Use this when the parser does not catch the parameters properly. For example:
129+
```json
130+
{"tabLabel": "탭 $tabCount개 중 $tabIndex번째"}
131+
```
132+
will generate
133+
```dart
134+
String tabLabel(String tabCount개, String tabIndex번째) => "탭 $tabCount개 중 $tabIndex번째";
135+
```
136+
Witch contains invalid Dart fields. In this case you should use
137+
```json
138+
{"tabLabel": "탭 ${tabCount}개 중 ${tabIndex}번째"}
139+
```
140+
141+
* Also you can escape the `$` sign with `\`
142+
```json
143+
{"checkout_message": "You have to pay \$$price"}
144+
```
129145
# Issues
130146

131147
There are some performance improvements and bug fixes that this plugin could use, so feel free to PR.

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group 'eu.long1.flutter'
7-
version '0.1.1'
7+
version '0.2.1'
88

99
repositories {
1010
mavenCentral()

src/main/kotlin/eu/long1/flutter/i18n/inspections/JsonKeysInspector.kt

+16-14
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,33 @@ import com.intellij.codeInspection.ex.BaseLocalInspectionTool
77
import com.intellij.json.psi.JsonProperty
88
import com.intellij.json.psi.JsonStringLiteral
99
import com.intellij.psi.PsiFile
10-
import com.intellij.psi.util.PsiTreeUtil
1110
import eu.long1.flutter.i18n.arb.ArbFileType
1211

1312
class JsonKeysInspector : BaseLocalInspectionTool() {
1413

1514
override fun checkFile(file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
1615
if (!isOnTheFly) return null
1716
if (file.fileType == ArbFileType) {
18-
return PsiTreeUtil.findChildrenOfType(file, JsonProperty::class.java).mapNotNull {
19-
val key = (it.nameElement as JsonStringLiteral).value
20-
if (!keyPattern.containsMatchIn(key)) {
21-
manager.createProblemDescriptor(
22-
it.nameElement,
23-
displayName,
24-
isOnTheFly,
25-
null,
26-
ProblemHighlightType.GENERIC_ERROR
27-
)
28-
} else null
29-
}.toTypedArray()
17+
return file.children.first().children.filterIsInstance<JsonProperty>()
18+
.filter { !it.name.startsWith("@") }
19+
.mapNotNull {
20+
val key = (it.nameElement as JsonStringLiteral).value
21+
22+
if (!key.startsWith('@') && !keyPattern.containsMatchIn(key)) {
23+
manager.createProblemDescriptor(
24+
it.nameElement,
25+
displayName,
26+
isOnTheFly,
27+
null,
28+
ProblemHighlightType.GENERIC_ERROR
29+
)
30+
} else null
31+
}.toTypedArray()
3032
}
3133
return null
3234
}
3335

34-
override fun getDisplayName(): String = "The string key must be a valid Dart field name."
36+
override fun getDisplayName(): String = "The string key can start with a letter or underscore (_), followed by any combination of those characters plus digits."
3537

3638
override fun getGroupDisplayName(): String = "Flutter I18n"
3739

src/main/kotlin/eu/long1/flutter/i18n/workers/I18nFileGenerator.kt

+30-36
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import eu.long1.flutter.i18n.arb.ArbFileType
1616
import eu.long1.flutter.i18n.files.FileHelpers
1717
import java.util.regex.Pattern
1818

19+
1920
class I18nFileGenerator(private val project: Project) {
2021

2122
private val psiManager = PsiManager.getInstance(project)
@@ -37,10 +38,12 @@ class I18nFileGenerator(private val project: Project) {
3738
val builder = StringBuilder()
3839
builder.append(i18nFileImports)
3940
builder.append("// ignore_for_file: non_constant_identifier_names\n// ignore_for_file: camel_case_types\n// ignore_for_file: prefer_single_quotes\n\n")
41+
builder.append("//This file is automatically generated. DO NOT EDIT, all your changes would be lost.\n")
4042
appendSClass(data["en"]!!, builder)
4143

44+
val className = "\$en"
4245
data.keys.forEach {
43-
if (it == "en") builder.append("class en extends S {\n const en();\n}\n\n")
46+
if (it == "en") builder.append("class $className extends S {\n const $className();\n}\n\n")
4447
else appendLangClass(it, data, builder)
4548
}
4649

@@ -100,12 +103,11 @@ class I18nFileGenerator(private val project: Project) {
100103
ids -= parametrized
101104

102105

103-
builder.append(
104-
"class $lang extends S {\n" +
105-
" const $lang();\n\n" +
106+
var className = "\$$lang"
106107

107-
" @override\n" +
108-
" TextDirection get textDirection => TextDirection.${if (rtl.contains(lang.split("_")[0])) "rtl" else "ltr"};\n\n"
108+
builder.append(
109+
"class $className extends S {\n const $className();\n\n " +
110+
"@override\n TextDirection get textDirection => TextDirection.${if (rtl.contains(lang.split("_")[0])) "rtl" else "ltr"};\n\n"
109111
)
110112

111113
ids.forEach { appendStringMethod(it, langMap[it]!!, builder) }
@@ -116,13 +118,10 @@ class I18nFileGenerator(private val project: Project) {
116118

117119
//for hebrew iw=he
118120
if (lang.startsWith("iw")) {
121+
className = "\$he_IL"
119122
builder.append(
120-
"class he_IL extends $lang {\n" +
121-
" const he_IL();\n\n" +
122-
123-
" @override\n" +
124-
" TextDirection get textDirection => TextDirection.rtl;\n" +
125-
"}"
123+
"class $className extends \$$lang {\n const $className();\n\n " +
124+
"@override\n TextDirection get textDirection => TextDirection.rtl;\n\n}"
126125
)
127126
}
128127
}
@@ -143,18 +142,13 @@ class I18nFileGenerator(private val project: Project) {
143142

144143
builder.append(delegateClassResolution)
145144
map.keys.forEach {
145+
146+
146147
//for hebrew iw=he
147148
if (it.startsWith("iw")) {
148-
builder.append(
149-
" case \"iw_IL\":\n" +
150-
" case \"he_IL\":\n" +
151-
" return SynchronousFuture<S>(const he_IL());\n"
152-
)
149+
builder.append(" case \"iw_IL\":\n case \"he_IL\":\n return SynchronousFuture<S>(const \$he_IL());\n")
153150
} else {
154-
builder.append(
155-
" case \"$it\":\n" +
156-
" return SynchronousFuture<S>(const $it());\n"
157-
)
151+
builder.append(" case \"$it\":\n return SynchronousFuture<S>(const \$$it());\n")
158152
}
159153
}
160154

@@ -206,6 +200,7 @@ class I18nFileGenerator(private val project: Project) {
206200
id: String, countsList: ArrayList<String>, valuesMap: HashMap<String, String>,
207201
builder: StringBuilder, isOverride: Boolean = true
208202
) {
203+
209204
val zero = countsList.contains("zero")
210205
val one = countsList.contains("one")
211206
val two = countsList.contains("two")
@@ -214,10 +209,13 @@ class I18nFileGenerator(private val project: Project) {
214209

215210

216211
val otherValue = valuesMap["${id}Other"] ?: valuesMap["${id}other"] ?: return
217-
val parameterName: String = {
218-
PARAMETER_MATCHER.reset(otherValue).find()
219-
normalizeParameter(PARAMETER_MATCHER.group())
220-
}()
212+
val parameterName: String =
213+
if (PARAMETER_MATCHER.reset(otherValue).find()) {
214+
normalizeParameter(PARAMETER_MATCHER.group())
215+
} else {
216+
"param"
217+
}
218+
221219

222220
if (isOverride) {
223221
builder.append(" @override\n")
@@ -321,27 +319,23 @@ class I18nFileGenerator(private val project: Project) {
321319
private fun getStringFromFile(file: PsiFile): HashMap<String, String>? {
322320
if (PsiTreeUtil.findChildOfType(file, PsiErrorElement::class.java) != null) return null
323321
val langMap = HashMap<String, String>()
324-
PsiTreeUtil.findChildrenOfType(file, JsonProperty::class.java).forEach {
325-
langMap[it.name] = it.value?.text!!.drop(1).dropLast(1)
326-
}
322+
323+
file.children.first().children.filterIsInstance<JsonProperty>()
324+
.filter { !it.name.startsWith("@") }
325+
.forEach { langMap[it.name] = it.value?.text!!.drop(1).dropLast(1) }
327326
return langMap
328327
}
329328

330329
companion object {
331330
private val PARAMETER_MATCHER =
332331
Pattern.compile(
333-
"(?<!\\\\)\\$\\{?[^}\\p{Punct}\\p{Space}\\p{sc=Han}\\p{sc=Hiragana}\\p{sc=Katakana}–]*}?"
332+
"(?<!\\\\)\\\$\\{?(.+?]*\\b)}?"
334333
)
335-
.matcher("")
334+
.matcher("")
336335

337336
// @formatter:off
338337
private const val i18nFileImports =
339-
"""/////////////////////////////////////////////////////////////
340-
// AUTO-GENERATED FILE – YOUR CHANGES WILL BE OVERWRITTEN! //
341-
// PLUGIN WEBSITE: https://github.com/long1eu/flutter_i18n //
342-
/////////////////////////////////////////////////////////////
343-
344-
import 'dart:async';
338+
"""import 'dart:async';
345339
346340
import 'package:flutter/foundation.dart';
347341
import 'package:flutter/material.dart';

src/main/resources/META-INF/plugin.xml

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<idea-plugin>
22
<id>FlutterI18n</id>
33
<name>Flutter i18n</name>
4-
<version>0.1.3</version>
4+
<version>0.2.1</version>
55
<vendor email="[email protected]">long1eu</vendor>
66
<depends>com.intellij.modules.json</depends>
77
<depends>com.intellij.modules.lang</depends>
@@ -14,7 +14,15 @@
1414
</description>
1515

1616
<change-notes><![CDATA[
17-
<p><strong><span style="font-size: 18px;">0.1.3 - 2018.3</span></strong></p>
17+
<p><strong><span style="font-size: 18px;">0.2</span></strong></p>
18+
<ul>
19+
<li><b>Braking change</b> - Lang classes are prefixed by $ sign. If you user them directly you should update them or use `S.of(context)`</li>
20+
<li>fix icelandic class</li>
21+
<li>add arb key checker, now the IDE will raise an error if you try to set an illegal key</li>
22+
<li>escape parameter in form of ${variable}</li>
23+
</ul>
24+
25+
<p><strong><span style="font-size: 18px;">0.1.1 - 2018.1</span></strong></p>
1826
<ul>
1927
<li>fix plurals searching</li>
2028
</ul>
@@ -76,7 +84,7 @@
7684
</change-notes>
7785

7886

79-
<idea-version since-build="183" until-build="183.*"/>
87+
<idea-version since-build="181" until-build="181.*"/>
8088

8189
<extensions defaultExtensionNs="com.intellij">
8290
<postStartupActivity implementation="eu.long1.flutter.i18n.workers.Initializer"/>

0 commit comments

Comments
 (0)